package zack.project.domain.strategy.service.rule.chain.impl;

import zack.project.domain.strategy.adapter.repository.IStrategyRepository;
import zack.project.domain.strategy.service.armory.IStrategyDispatch;
import zack.project.domain.strategy.service.rule.chain.AbstractLogicChain;
import zack.project.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import zack.project.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**权重过滤节点
 * @author A1793
 */
@Slf4j
@Component("rule_weight")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

public class RuleWeightLogicChain extends AbstractLogicChain {

    @Resource
    private IStrategyRepository repository;

    @Resource
    private IStrategyDispatch strategyDispatch;

    private final IAnalytical analyticalNotEqual = new AnalyticalNotEqual();
    @Override
    protected String ruleModels() {
        return DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode();
    }

    @Override
    public DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId) {
        log.info("抽奖责任链-权重开始 userId:{} strategyId:{} ruleModel:{}", userId, strategyId, ruleModels());
        //根据抽奖策略id和ruleModels()【返回"rule_weight"】查找数据库表{strategy_rule}获取对应策略规则的rule_value（抽奖权重配置）
        String ruleValue = repository.queryStrategyRuleValue(strategyId,ruleModels());
        //获得次数权重map
        Map<Integer, String> analyticalValueGroup = getAnalyticalValue(ruleValue);
        if( analyticalValueGroup == null || analyticalValueGroup.isEmpty()){
            log.warn("抽奖责任链-权重告警【策略配置权重，但ruleValue未配置相应值】 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModels());
            return next().logic(userId, strategyId);
        }
        //查询用户已抽奖次数
        //根据strategyId去查询{raffle_activity}获取一个策略id对应的抽奖活动实体，
        // 根据这个活动实体的活动id和userId去查询{raffle_activity_account}，查询该用户在该活动上的活动账户实体
        //根据该实体的totalCount和totalSurplus获取用户的抽奖次数
        Integer userScore = repository.queryActivityAccountTotalUseCount(userId,strategyId);
        //在analyticalValueGroup表的键(权重配置的抽奖次数)中，
        // 找一个小于当前用户抽奖次数的最大值获取一个key(这个key是:抽奖次数:奖品id列表，是在活动装配的时候预热到redis里的)，
        // 根据这个key获取一个权重概率表进行随机抽奖
        String nextKey = analyticalNotEqual.getAnalyticalValue(analyticalValueGroup, userScore);
        //如果有对应的权重次数就进行抽奖并返回
        if(nextKey != null){
                Integer awardId = strategyDispatch.getRandomAwardId(strategyId, nextKey);
                log.info("抽奖责任链-权重接管 userId: {} strategyId: {} ruleModel: {} awardId: {}", userId, strategyId, nextKey, awardId);

                    return DefaultChainFactory.StrategyAwardVO.builder()
                            .awardId(awardId)
                            .logicModel(ruleModels())
                            .awardRuleValue(ruleValue)
                            .build();
        }
        log.info("抽奖责任链-权重放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModels());
        //没被黑名单拦截也没有对应权重走默认节点
        return next().logic(userId, strategyId);

    }

    /**
     * 解析在数据库表{strategy_rule}中查询到的{rule-value}中的权重配置{次数1:奖品id列表1 次数2:奖品id列表2 ····}
     * {rule_value} : {10:102,103 70:106,107 1000:104,105}.
     * 返回一个(次数,{rule_value[i]})entry的map
     * @param ruleValue
     * @return
     */
    private Map<Integer,String> getAnalyticalValue(String ruleValue){

        String[] ruleValueGroups = ruleValue.split(Constants.SPACE);
        Map<Integer,String> resultMap = new HashMap<>(ruleValueGroups.length);

        for(String ruleValueGroup : ruleValueGroups){

            if (ruleValue == null || ruleValue.isEmpty()) {
                return resultMap;
            }

            String[] part = ruleValueGroup.split(Constants.COLON);

            if(part.length != 2){
                throw new IllegalArgumentException("rule_weight rule_rule invalid input format" + ruleValueGroup);
            }

            resultMap.put(Integer.parseInt(part[0]),ruleValueGroup);
        }
        return resultMap;

    }
    public interface IAnalytical{
        String getAnalyticalValue(Map<Integer, String> analyticalValueGroup, Integer userScore);
    }

    /**
     * 当用户的抽奖次数与analyticalValueGroup中的某个键值正好 相等时走这个
     */
    static class  AnalyticalEqual implements IAnalytical{
        @Override
        public String getAnalyticalValue(Map<Integer, String> analyticalValueGroup, Integer userScore) {
             return analyticalValueGroup.get(userScore);
        }
    }

    /**
     * 当用户抽奖次数与analyticalValueGroup中的键不等时，找到第一个小于抽奖次数的键返回其中的奖励配置
     */
    static class  AnalyticalNotEqual implements IAnalytical{
        @Override
        public String getAnalyticalValue(Map<Integer, String> analyticalValueGroup, Integer userScore) {
            List<Integer> analyticalValueGroupKeys = new ArrayList<>(analyticalValueGroup.keySet());
            Collections.sort(analyticalValueGroupKeys);
            //降序排序并收集小于userScore的然后找到第一个（所有小于userScore的数中找一个最大的）
            Integer nextKey = analyticalValueGroupKeys.stream()
                    .sorted(Collections.reverseOrder())
                    .filter(s -> s <= userScore)
                    .findFirst()
                    .orElse(null);

            return analyticalValueGroup.get(nextKey);
        }
    }

}
